import { Injectable, Logger } from '@nestjs/common';
import { Connection, Keypair, PublicKey, Transaction, SystemProgram } from '@solana/web3.js';
import { Program, AnchorProvider, Wallet, BN, Idl } from '@coral-xyz/anchor';
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from '@solana/spl-token';
import { ContractsConfig } from '../config/contracts.config';
import * as fs from 'fs';
import * as path from 'path';

@Injectable()
export class BondingCurveService {
  private readonly logger = new Logger(BondingCurveService.name);
  private connection: Connection;
  private program: Program | null = null;

  constructor() {
    this.connection = new Connection(
      process.env.SOLANA_RPC_URL || 'https://api.devnet.solana.com',
      'confirmed'
    );
  }

  /**
   * 初始化 Anchor 程序
   */
  private async initializeProgram(wallet: Keypair): Promise<Program> {
    if (this.program) {
      return this.program;
    }

    const provider = new AnchorProvider(
      this.connection,
      new Wallet(wallet),
      AnchorProvider.defaultOptions()
    );

    // 加载 IDL 文件
    const idlPath = path.join(__dirname, '../idl/ignite_contracts.json');
    const idlFile = fs.readFileSync(idlPath, 'utf8');
    const idl = JSON.parse(idlFile) as Idl;

    this.program = new Program(idl, provider);
    return this.program;
  }

  /**
   * 初始化工厂合约（如果还未初始化）
   */
  async initializeFactoryIfNeeded(authorityKeypair?: Keypair): Promise<boolean> {
    try {
      const authority = authorityKeypair || Keypair.generate();
      const program = await this.initializeProgram(authority);

      const [factoryPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("factory")],
        program.programId
      );

      // 检查工厂是否已经存在
      try {
        const factoryAccountInfo = await this.connection.getAccountInfo(factoryPda);
        if (factoryAccountInfo) {
          this.logger.log('Factory already initialized:', factoryPda.toString());
          return false; // 已经初始化
        }
      } catch (error) {
        // 工厂不存在，需要初始化
        this.logger.log('Factory not found, initializing...');
      }

      // 创建初始化工厂指令
      const initializeFactoryIx = await program.methods
        .initializeFactory()
        .accounts({
          factory: factoryPda,
          authority: authority.publicKey,
          systemProgram: SystemProgram.programId,
        })
        .instruction();

      const transaction = new Transaction().add(initializeFactoryIx);
      transaction.feePayer = authority.publicKey;
      transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;

      // 如果没有提供权限密钥，我们需要返回交易让前端签名
      if (!authorityKeypair) {
        this.logger.warn('No authority keypair provided for factory initialization');
        return false;
      }

      // 签名并发送交易
      transaction.sign(authority);
      const signature = await this.connection.sendTransaction(transaction, [authority]);
      await this.connection.confirmTransaction(signature);

      this.logger.log('Factory initialized successfully:', {
        factory: factoryPda.toString(),
        signature
      });

      return true; // 新初始化
    } catch (error) {
      this.logger.error('Failed to initialize factory:', error);
      throw error;
    }
  }

  /**
   * 创建代币（通过联合曲线合约）
   */
  async createTokenWithBondingCurve(
    name: string,
    symbol: string,
    uri: string,
    creatorKeypair: Keypair
  ): Promise<{ tokenMint: PublicKey; transaction: string }> {
    try {
      this.logger.log('Creating token with bonding curve:', { name, symbol });

      const program = await this.initializeProgram(creatorKeypair);
      
      // 生成代币 mint 地址
      const tokenMint = Keypair.generate();

      // 计算 PDA 地址
      const [factoryPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("factory")],
        program.programId
      );

      const [tokenPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("token"), tokenMint.publicKey.toBuffer()],
        program.programId
      );

      const [bondingCurvePda] = PublicKey.findProgramAddressSync(
        [Buffer.from("curve"), tokenMint.publicKey.toBuffer()],
        program.programId
      );

      // 创建代币指令
      // 注意：total_supply 应该是以最小单位计算的总量
      // 对于9位小数的代币，1个完整代币 = 10^9 最小单位
      // 所以10亿个完整代币 = 10^9 * 10^9 = 10^18 最小单位
      const totalSupply = new BN(1000000000).mul(new BN(1000000000)); // 10^18 最小单位
      
      this.logger.log(`Creating bonding curve token with total supply: ${totalSupply.toString()}`);
      
      const createTokenIx = await program.methods
        .createToken(name, symbol, uri, totalSupply)
        .accounts({
          factory: factoryPda,
          bondingCurve: bondingCurvePda,
          mint: tokenMint.publicKey,
          creator: creatorKeypair.publicKey,
          tokenProgram: TOKEN_PROGRAM_ID,
          systemProgram: SystemProgram.programId,
          rent: new PublicKey('SysvarRent111111111111111111111111111111111'),
        })
        .instruction();

      const transaction = new Transaction().add(createTokenIx);
      transaction.feePayer = creatorKeypair.publicKey;
      transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;

      // 签名并发送交易
      transaction.sign(creatorKeypair, tokenMint);
      const signature = await this.connection.sendTransaction(transaction, [creatorKeypair, tokenMint]);
      await this.connection.confirmTransaction(signature);

      this.logger.log('Token created successfully:', {
        mint: tokenMint.publicKey.toString(),
        signature
      });

      return {
        tokenMint: tokenMint.publicKey,
        transaction: signature
      };

    } catch (error) {
      this.logger.error('Failed to create token with bonding curve:', error);
      throw error;
    }
  }

  /**
   * 购买代币（通过联合曲线）
   */
  async buyToken(
    tokenMint: PublicKey,
    solAmount: number,
    buyer: PublicKey
  ): Promise<{ transaction: string; }> {
    try {
      this.logger.log('Buying tokens:', { tokenMint: tokenMint.toString(), solAmount });

      // 首先检查代币是否已经毕业
      const tokenInfo = await this.getTokenInfo(tokenMint);
      if (tokenInfo.isGraduated) {
        throw new Error(`此代币已经毕业，无法通过联合曲线系统进行交易。请前往 Raydium 或其他 DEX 进行交易。`);
      }

      // 注意：这个方法目前只返回序列化的交易，不执行交易
      // 在实际应用中，前端会签名并提交这个交易
      const program = await this.initializeProgram(Keypair.generate()); // 使用临时的 keypair

      // 确保工厂已初始化
      await this.initializeFactoryIfNeeded();

      // 计算相关账户地址
      const [factoryPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("factory")],
        program.programId
      );

      const [bondingCurvePda] = PublicKey.findProgramAddressSync(
        [Buffer.from("curve"), tokenMint.toBuffer()],
        program.programId
      );

      const buyerTokenAccount = await getAssociatedTokenAddress(
        tokenMint,
        buyer
      );

      // 创建购买指令
      const buyIx = await program.methods
        .buy(new BN(solAmount * 1_000_000_000)) // 转换为 lamports
        .accounts({
          factory: factoryPda,
          bondingCurve: bondingCurvePda,
          mint: tokenMint,
          buyer: buyer,
          buyerTokenAccount: buyerTokenAccount,
          tokenProgram: TOKEN_PROGRAM_ID,
          systemProgram: SystemProgram.programId,
          associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
          rent: new PublicKey('SysvarRent111111111111111111111111111111111'),
        })
        .instruction();

      const transaction = new Transaction().add(buyIx);
      transaction.feePayer = buyer;
      transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;

      const serializedTx = transaction.serialize({
        requireAllSignatures: false,
      });

      return {
        transaction: Buffer.from(serializedTx).toString("base64"),
      };

    } catch (error) {
      this.logger.error('Failed to buy tokens:', error);
      throw error;
    }
  }

  /**
   * 销售代币（通过联合曲线）
   */
  async sellToken(
    tokenMint: PublicKey,
    tokenAmount: number,
    seller: PublicKey
  ): Promise<{ transaction: string; }> {
    try {
      this.logger.log('Selling tokens:', { tokenMint: tokenMint.toString(), tokenAmount });

      // 首先检查代币是否已经毕业
      const tokenInfo = await this.getTokenInfo(tokenMint);
      if (tokenInfo.isGraduated) {
        throw new Error(`此代币已经毕业，无法通过联合曲线系统进行交易。请前往 Raydium 或其他 DEX 进行交易。`);
      }

      const program = await this.initializeProgram(Keypair.generate());

      // 确保工厂已初始化
      await this.initializeFactoryIfNeeded();

      const [factoryPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("factory")],
        program.programId
      );

      const [bondingCurvePda] = PublicKey.findProgramAddressSync(
        [Buffer.from("curve"), tokenMint.toBuffer()],
        program.programId
      );

      const sellerTokenAccount = await getAssociatedTokenAddress(
        tokenMint,
        seller
      );

      const sellIx = await program.methods
        .sell(new BN(tokenAmount))
        .accounts({
          factory: factoryPda,
          bondingCurve: bondingCurvePda,
          mint: tokenMint,
          seller: seller,
          sellerTokenAccount: sellerTokenAccount,
          tokenProgram: TOKEN_PROGRAM_ID,
          systemProgram: SystemProgram.programId,
        })
        .instruction();

      const transaction = new Transaction().add(sellIx);
      transaction.feePayer = seller;
      transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;

      const serializedTx = transaction.serialize({
        requireAllSignatures: false,
      });

      return {
        transaction: Buffer.from(serializedTx).toString("base64"),
      };

    } catch (error) {
      this.logger.error('Failed to sell tokens:', error);
      throw error;
    }
  }

  /**
   * 获取代币价格
   */
  async getTokenPrice(tokenMint: PublicKey): Promise<number> {
    try {
      const program = await this.initializeProgram(Keypair.generate());
      
      const [bondingCurvePda] = PublicKey.findProgramAddressSync(
        [Buffer.from("curve"), tokenMint.toBuffer()],
        program.programId
      );

      const bondingCurveAccountInfo = await this.connection.getAccountInfo(bondingCurvePda);
      
      if (!bondingCurveAccountInfo) {
        this.logger.warn('Bonding curve not found, returning default price');
        return 0.0000001; // 默认初始价格
      }

      // 手动解析账户数据
      const data = bondingCurveAccountInfo.data;
      if (data.length < 8 + 32 + 32 + 8 + 8 + 8) {
        this.logger.warn('Bonding curve data too short, returning default price');
        return 0.0000001;
      }

      // 跳过 discriminator (8 bytes) + mint (32 bytes) + creator (32 bytes)
      let offset = 8 + 32 + 32;
      
      // 读取 total_supply (8 bytes)
      const totalSupply = Number(data.readBigUInt64LE(offset));
      offset += 8;
      
      // 读取 circulating_supply (8 bytes)
      const circulatingSupply = Number(data.readBigUInt64LE(offset));
      offset += 8;
      
      // 读取 sol_reserves (8 bytes)
      const solReserves = Number(data.readBigUInt64LE(offset));
      
      this.logger.log('Bonding curve data:', { 
        totalSupply, 
        circulatingSupply, 
        solReserves,
        tokenMint: tokenMint.toString()
      });
      
      // 计算基于联合曲线的当前边际价格（SOL 计价）
      if (circulatingSupply === 0) {
        return 0.0000001; // 初始价格（SOL）
      }
      
      // 根据智能合约的二次联合曲线公式：price = slope * supply / 1e9
      // 注意：这里 supply 是原始单位，price 是每个原始单位的价格
      const defaultSlope = 1000000; // 默认 slope 值，与合约初始化时一致
      
      // 边际价格（每个原始单位）
      const pricePerRawUnit = (defaultSlope * circulatingSupply) / 1_000_000_000;
      
      // 转换为每个完整代币的价格（1个完整代币 = 1e9个原始单位）
      const priceInSol = pricePerRawUnit / 1_000_000_000;
      
      this.logger.log('SOL-based price calculation:', {
        solReserves: solReserves / 1_000_000_000,
        circulatingSupply: circulatingSupply / 1_000_000_000,
        priceInSol,
        tokenMint: tokenMint.toString()
      });
      
      return Math.max(priceInSol, 0.0000001); // 确保价格不为零
      
    } catch (error) {
      this.logger.error('Failed to get token price:', error);
      return 0.0000001; // 返回默认价格
    }
  }

  /**
   * 获取代币详细信息
   */
  async getTokenInfo(tokenMint: PublicKey): Promise<any> {
    try {
      const program = await this.initializeProgram(Keypair.generate());
      
      const [bondingCurvePda] = PublicKey.findProgramAddressSync(
        [Buffer.from("curve"), tokenMint.toBuffer()],
        program.programId
      );

      const bondingCurveAccountInfo = await this.connection.getAccountInfo(bondingCurvePda);
      
      if (!bondingCurveAccountInfo) {
        throw new Error(`此代币 (${tokenMint.toString()}) 不是通过联合曲线系统创建的，无法在此平台交易。请确认代币地址是否正确，或使用创建代币功能来创建新的代币。`);
      }

      // 手动解析账户数据
      const data = bondingCurveAccountInfo.data;
      
      // Anchor 数据布局：8字节 discriminator + 结构体数据
      let offset = 8; // 跳过 discriminator
      
      // mint: Pubkey (32 bytes)
      offset += 32;
      
      // creator: Pubkey (32 bytes)  
      offset += 32;
      
      // total_supply: u64 (8 bytes)
      const totalSupply = Number(data.readBigUInt64LE(offset));
      offset += 8;
      
      // circulating_supply: u64 (8 bytes)
      const circulatingSupply = Number(data.readBigUInt64LE(offset));
      offset += 8;
      
      // sol_reserves: u64 (8 bytes)
      const solReserves = Number(data.readBigUInt64LE(offset));
      offset += 8;
      
      // is_graduated: bool (1 byte)
      const isGraduated = data.readUInt8(offset) === 1;
      
      this.logger.log(`Parsed bonding curve data for ${tokenMint.toString()}:`);
      this.logger.log(`- Total Supply: ${totalSupply}`);
      this.logger.log(`- Circulating Supply: ${circulatingSupply}`);
      this.logger.log(`- SOL Reserves: ${solReserves}`);
      this.logger.log(`- Is Graduated: ${isGraduated}`);

      const priceInSol = await this.getTokenPrice(tokenMint);
      
      // 计算市值：使用总供应量计算完全稀释市值
      const actualTotalSupply = totalSupply / 1_000_000_000; // 转换为实际单位
      
      // 对于旧代币可能有错误的供应量，使用标准值作为备选
      const standardSupply = actualTotalSupply < 1000 ? 1000 : actualTotalSupply; // 至少1000个代币
      
      const marketCapInSOL = standardSupply * priceInSol;
      
      this.logger.log(`Market cap calculation: ${standardSupply} tokens × ${priceInSol} SOL = ${marketCapInSOL} SOL`);

      return {
        mint: tokenMint.toString(),
        price: priceInSol,
        totalSupply: actualTotalSupply, // 使用转换后的实际总供应量
        circulatingSupply: circulatingSupply / 1_000_000_000,
        solReserves: solReserves / 1_000_000_000, // 转换为 SOL
        marketCap: marketCapInSOL,
        isGraduated,
      };
    } catch (error) {
      this.logger.error('Failed to get token info:', error);
      throw error;
    }
  }

  /**
   * 获取价格历史数据（用于蜡烛图）
   */
  async getPriceHistory(tokenMint: PublicKey, timeframe: string = '1h'): Promise<any[]> {
    try {
      // 这里应该从数据库或缓存中获取历史价格数据
      // 目前返回模拟数据
      const now = Date.now();
      const hour = 60 * 60 * 1000;
      const mockData = [];

      for (let i = 23; i >= 0; i--) {
        const timestamp = now - (i * hour);
        const basePrice = 0.001 + Math.random() * 0.0005;
        mockData.push({
          timestamp,
          open: basePrice,
          high: basePrice * (1 + Math.random() * 0.1),
          low: basePrice * (1 - Math.random() * 0.1),
          close: basePrice + (Math.random() - 0.5) * 0.0002,
          volume: Math.floor(Math.random() * 10000)
        });
      }

      return mockData;
    } catch (error) {
      this.logger.error('Failed to get price history:', error);
      return [];
    }
  }
}
